// in server1.js
const http = require("node:http"); // reference: Node.js HTTP Module
const server = http.createServer((request, response) => {
response.writeHead(200, {"Content-Type": "text/plain"});
response.write("<h1>Hello World!</h1>");
response.end();
});
server.listen(8080); // Use your port number instead of 8080.
How to run?$ node server1.js in the command-line interfacehttp://cs.tru.ca:8080 or http://198.162.21.132:8080 on a web browser, not https.require() do?.listen() do?.createServer()?request and response, are passed.
request holds all the information related to the HTTP request sent from a client, and
response is used to send data back to the client.request, Node.js IncomingMessage Objectresponse, Node.js HTTP ServerResponse Object
exportsserver2.js into a module?
// server2.js
const PORT_NO = 8080; // Use your port number, not 8080
const http = require('node:http');
const start = function() {
const server = http.createServer(
(request, response) => { // request: from the client; response: to the client
response.writeHead(200, {"Content-Type": "text/plain"});
response.write("<h1>Hello World!</h1>");
response.end();
}
);
server.listen(PORT_NO);
}
// Something more
exports.start = start;
index2.js?
// index.2.js
const server = require('./server2'); // server2.js; the variable server represents all the exported functions in server2.js
server.start(); // .start() is the function that is defined in server2.js and exported.
$ node ./index2.js
request and response to Router?router3.js
const route = function(request, response) {
response.writeHead(200, {'Content-Type': 'text/plain'}); // Plain text, not html text, will be sent back to the client
response.write("<h1>Hello World!</h1>");
response.end();
}
exports.route = route;
index3.js, and pass it to the server.index3.js
const server = require('./server3');
const router = require('./router3');
server.start(router);
server3.js
const PORT_NO = 8080; // Use your port number, not 8080
const http = require('node:http');
const start = function(router) { // Do we have to require 'router'?
const server = http.createServer(
(request, response) => { // request: from the client; response: to the client
router.route(request, response);
}
);
server.listen(PORT_NO);
}
// Something more
exports.start = start;

const server = require('./server');
const router = require('./router');
server.start(router);
const PORT_NO = 8080; // Use your port number
const http = require('node:http');
const start = function(router) {
const server = http.createServer(
(request, response) => { // request: from the client; response: to the client
router.route(request, response);
}
);
server.listen(PORT_NO);
}
// Something more
exports.start = start;
const route = function(request, response) {
response.writeHead(200, {'Content-Type': 'text/plain'}); // Plain text, not html text, will be sent back to the client
response.write("<h1>Hello World!</h1>");
response.end();
}
exports.route = route;
request?
(Note that pathname obtained from url.parse(request.url).pathname starts with '/'.)
const url = require("node:url"); // reference: Node.js URL Module
const route = function (request, response) {
const pathname = decodeURI(url.parse(request.url).pathname); // decodeURI(): a JS built-in function
// reference: JavaScript decodeURI()
response.writeHead(200, {"Content-Type": "text/html"}); // text/html, not text/plain, in this example
response.write("<h1>Hello World!</h1><br>");
response.write(pathname + "<br>");
response.end();
}
exports.route = route;
request.url, so that
TRUWSJS can access the resource.
"tilde-expansion" module.
(Note that this module supports a callback function, but not a Promise, i.e., async-await.)
After you install the module as $ npm install tilde-expansion in the current working directory,
const tilde = require("tilde-expansion"); // reference: tilde-expansion
tilde(username, function(expanded) { // E.g., "~tom" -> "/home/students/tom"
console.log(expanded);
});
request.url.request.url..sjs files.
.sjs files..sjs files..sjs files:
handler() should be exported.
This function is the starting point in a server-side JS program, like main() in Java.
// In each .sjs file,
// _GET: object for the GET data
// _POST: object for the POST data
// callback: callback function to pass a message back to the client
const handler = function(_GET, _POST, callback) {
...
}
exports.handler = handler;
handler(), the callback function should be used to send any message back to the client.
.handler() will be invoked to execuse them.
The return message from .handler() will be sent back to the client.
handler(),
in which the application code is fully responsible how to use request and how to send messages or data back to the client.
const handler = async function(request, response) {
...
}
export handler;
request.url?
const url = require('node:url'); // reference: Node.js URL Module
const route = function (request, response)
{
const pathname = decodeURI(url.parse(request.url).pathname); // encodeURI(), decodeURI()
// What does decodeURI() do?
...
}
$ npm install tilde-expansion
const tilde = require('tilde-expansion'); // reference: tilde-expansion
const words = pathname.split('/'); // words[0] is '' and words[1] is '~...' because pathname always starts with '/'. E.g, /~tom/...
if (words[1] != undefined && words[???][???] == '~') // e.g., pathname is /~tom/...
tilde(words[1], function(expanded) {
let new_pathname = expanded + '/???'; // 'public_html' should be included.
for (let i = 2; i < words.length; i++)
new_pathname += '/' + ???;
});
else
let new_pathname = "/var/www/html" + pathname;
const url = require('node:url');
const route = function (request, response)
{
let pathname = decodeURI(url.parse(request.url).pathname);
const tilde = require('tilde-expansion');
let words = pathname.split('/'); // words[0] is '' and words[1] is '~...' because pathname starts with '/'.
// Case 1
if (words[???] != undefined && words[???][???] == '~') { // e.g., /~tom/... => '', '~tom', ...
tilde(words[1], function(expanded) { // Note that a callback function is used.
let new_pathname = expanded + '/???'; // 'public_html' should be included.
for (let i = 2; i < words.length; i++)
new_pathname += '/' + ???;
????
});
???? // really necessary?
}
// Case 2
else {
let new_pathname = "/var/www/html" + pathname;
????
}
}
const proceed_with_resolved_pathname = function (req, res, pathname) {
???? // send the pathname to the client
}
if (pathname.endsWith("/")) {
pathname = pathname + "/index.html";
...
}
const url = require('node:url');
function route(request, response)
{
let pathname = decodeURI(url.parse(request.url).pathname);
let tilde = require('tilde-expansion');
let words = pathname.split('/'); // words[0] is '' and words[1] is '~...' because pathname starts with '/'.
// Case 1
if (words[1] != undefined && words[1][0] == '~') { // e.g., /~tom/...
tilde(words[1], function(expanded) { // Note that a callback function is used.
let new_pathname = expanded + '/???'; // 'public_html' should be included.
for (let i = 2; i < words.length; i++)
new_pathname += '/' + words[i];
proceed_with_resolved_pathname(request, response, new_pathname);
});
}
// Case 2
else {
let new_pathname = "/var/www/html" + pathname;
proceed_with_resolved_pathname(request, response, new_pathname);
}
}
function proceed_with_resolved_pathname(req, res, pathname)
{
// Case 3
???? // what if pathname ends with '/'?
res.writeHead(200, {"Content-Type": "text/html"}); // text/html, not text/plain, in this example
res.write("<h1>Hello World!</h1><br>");
res.write(??? + "<br>");
res.end();
}
const path = require("node:path"); // reference: Node.js Path Module
let fileordirname = path.basename(pathname);
let words = fileordirname.split(".");
if (words.length > 1) { // if there is ...
let extension = words[words.length - 1];
...
} else { // if there is no ...
...
}
const path = require("node:path");
let fileordirname = path.basename(pathname);
let words = fileordirname.split(".");
let extension;
if (words.length > 1) {
extension = words[words.length - 1];
} else {
pathname = ????
extension = ????
}
...
const url = require('node:url');
function route(request, response)
{
let pathname = decodeURI(url.parse(request.url).pathname);
let tilde = require('tilde-expansion');
let words = pathname.split('/'); // words[0] is '' and words[1] is '~...' because pathname starts with '/'.
// Case 1
if (words[1] != undefined && words[1][0] == '~') { // e.g., /~tom/...
tilde(words[1], function(expanded) { // Note that a callback function is used.
let new_pathname = expanded + '/???'; // 'public_html' should be included.
for (let i = 2; i < words.length; i++)
new_pathname += '/' + words[i];
proceed_with_resolved_pathname(request, response, new_pathname);
});
}
// Case 2
else {
let new_pathname = "/var/www/html" + pathname;
proceed_with_resolved_pathname(request, response, new_pathname);
}
}
function proceed_with_resolved_pathname(req, res, pathname)
{
let extension;
// Case 3
if (pathname.endsWith("/")) {
pathname = pathname + "/index.html";
extension = "html";
}
// Case 4
else {
????
}
res.writeHead(200, {"Content-Type": "text/html"}); // text/html, not text/plain, in this example
res.write("<h1>Hello World!</h1><br>");
res.write("Pathname: " + ??? + "<br>");
res.write("File extension: " + ??? + "<br>");
res.end();
}
<HTML>
<HEAD>
<meta HTTP-EQUIV="REFRESH" content="0; url=http://www.tru.ca:80/science/programs/compsci.html">
</HEAD>
</HTML>
response.writeHead(301, {'Location': different_location});
const fs = require('node:fs');
const filename = 'test.html';
fs.readFile(filename, 'utf8', function(err, content) {
if (!err) {
console.log(content);
}
else {
console.log(err);
}
});
router.js.
// In proceed_with_resolved_pathname(),
...
if (extension != 'sjs' && extension != 'php') { // Let's not send server-side code back.
const fs = require('node:fs'); // reference: Node.js File System Module
if (extension == 'html' || extension == 'htm')
content_type = 'text/html';
else if ...
...
fs.readFile(pathname, 'utf8', function(err, content) {
if (!err) {
response.writeHead(200, {'Content-type': content_type}); // 200: OK; important
response.write(content);
response.end();
}
else {
response.writeHead(404, {'Content-type': "text/html"}); // 4xx: client error
response.end("<html><body><h1>Not Found</h1><p>The requested URL was not found on this server.</p><hr></body></html>");
}
});
}
// A server-side JavaScript file, e.g., echo_server.sjs and calculator_server.sjs:
// _GET: object for the GET data
// _POST: object for the POST data
// callback: callback function to pass a message back to the client
function handler(_GET, _POST, callback) {
...
}
exports.handler = handler;
.sjs?
// In proceed_with_resolved_pathname(),
...
if (extension == 'sjs') {
if (req.method.toLowerCase() == "get") {
... // retrieve the GET data
proceed_sjs(req, res, pathname, _GET, {}); // For Task 5
}
else if (req.???? == '???') {
... // retrieve the POST data
proceed_sjs(req, res, pathname, {}, _POST); // For Task 5
}
}
// proceed_sjs()
function proceed_sjs(req, res, pathname, _GET, _POST) { // For Task 5
...
}
querystring - Node.js Query String Module.'http://test.com/start.html?command=Login&username=foo&password=topsecret'.const querystring = require("node:querystring");decodeURI(url.parse(request.url).pathname) → '/start.html'decodeURI(url.parse(request.url).query) → 'command=Login&username=foor&password=topsecret'let _GET = querystring.parse(decodeURI(url.parse(request.url).query)) → { command:'Login', username:'foo', password:'topsecret' }_GET['command'] → 'Login'JSON.stringfy()proceed_sjs().
(Note that if your Node WebServer is running as a part of assignment, use a different port number for this Trial.)
request' is an Event Emitter object, and
it will emit the 'data' event when the POST data was received.
When there is no more POST data to retrieve, the 'end' event is emitted..on() method on 'request' can used to listen to these events.
let query = '';
let _POST = {}; // not [] ?
request.on('data', function(chunk) { // request is an Event Emitter.
query += chunk;
});
request.???('end', function() { // the end of the data
if (query != '')
_POST = ???? // parsing using the 'querystring' module. Check the example in the above 'GET' case.
...
});
_POST.proceed_sjs().
// In proceed_sjs(req, res, pathname, _GET, _POST),
const sjs = ???(pathname); // 'pathname' is a sjs script file. This file is required as a module. For example, test.sjs.
// We assume that all '.sjs' programs have the function, handler(),
// that is the starting point like main() in Java and C/C++ programs.
// _GET: object for the GET query; _POST: object for the POST query
// For example, { command:'Login', username:'foo', password:'topsecret' }
sjs.handler(_GET, _POST, function(content) { // callback function to get the result from the sjs program
....
res.???("" + content);
res.???();
});
//---- A .sjs file that is indicated by pathname in proceed_sjs() ----
function handler(_GET, _POST, callback) {
let content;
if (_GET['command'] == 'Login') // For example
...
....
???(content); // Don't forget that this function should pass a message back through callback
}
???.??? = handler;
try-catch?throw?isNaN() do?
const divide = function(x, y) {
if (y === 0)
// throw new Error({name: 'Divide', message: "Can't divide by zero"}); // or
throw {name: 'Divide', message: "Can't divide by zero"};
else
return x/y;
};
try {
let result = divide(4, 2);
...
}
??? (e) {
console.log(e.???); // Not just e. e.name, e.stack, e.message, ...
}
// In proceed_sjs(req, res, pathname, _GET, _POST),
??? {
const sjs = require(pathname); // We assume all '.sjs' programs have handler().
sjs.handler(_GET, _POST, function(html_content) {
....
});
} ??? (err) {
....
}
// In proceed_sjs(req, res, pathname, _GET, _POST),
??? {
const sjs = require(pathname); // We assume all '.sjs' programs have handler().
let ended = false;
sjs.handler(_GET, _POST, function(html_content) {
if (!ended) {
.... // write(), ..., end()
delete require.cache[require.resolve(pathname)]; // in order to unload this .sjs module;
// https://nodejs.org/api/modules.html#requirecache
ended = true; // when the callback function is invoked multiple times from the sjs app,
// response.end() is invoked again.
// It causes an error that cannot be caught.
}
});
} catch (err) {
....
???[???]; // in order to unload this .sjs module
}
const https = require('node:https');
const fs = require('node:fs');
let options = {
key: fs.readFileSync('test/fixtures/keys/agent2-key.pem'), // Key
cert: fs.readFileSync('test/fixtures/keys/agent2-cert.pem') // Certificate
};
https.???(options, function (req, res) { // similar to http
res.writeHead(200);
res.end("Hello world!\n");
}).???(8443);
const https = require('node:https'),
pem = require('pem');
pem.createCertificate({days:100, selfSigned:true}, function(err, keys) {
if (!err)
https.???({key: keys.serviceKey, cert: keys.certificate}, function(req, res) {
res.writeHead(200);
res.end("Hello world!\n")
}).???(8443);
});
const http = require('node:http');
const https = require('node:https');
const pem = require('pem');
//const ws_server = require('./ws_server');
function start(route)
{
// HTTP server
const server = http.???(function(request, response) {
route(request, response);
});
server.???(8080);
// WebSocket server integrated with HTTP server
// It will be discussed later.
//ws_server.start(server); // WebSocket server: ws Node.js module
// HTTPS server
pem.createCertificate({days:100, selfSigned:true}, function(err, keys) {
if (!err)
https.???({key: keys.serviceKey, cert: keys.certificate}, function(request, response) {
???(???, ???);
}).???(8443);
});
}
???.start = start;